package com.bb.flow.service.imp;

import bigbeard.tools.thread.pools.ThreadPoolFactory;
import com.bb.flow.api.type.ExecuteState;
import com.bb.flow.config.AbstractConfigNode;
import com.bb.flow.config.FlowInfo;
import com.bb.flow.config.enumtype.NodeType;
import com.bb.flow.service.api.NodeExecutor;
import com.bb.flow.service.api.NodeStateCallback;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.ExecutorService;


/**
 * @author bigbeard
 * 负责节点执行,包括等待,不负责节点之间的流转
 */
public abstract class AbstractNodeExecutor implements NodeExecutor {
    private final static Logger logger = LoggerFactory.getLogger(AbstractNodeExecutor.class);
    protected final ExecutorService executorService;
    /**
     * 当前流程
     */
    protected FlowInfo currentFlowInfo;
    /**
     * 节点状态回调
     */
    private NodeStateCallback nodeStateCallback;
    /**
     * 当前节点
     */
    private AbstractConfigNode currentNode;

    public AbstractNodeExecutor() {
        executorService = ThreadPoolFactory.createThreadPool(getThreadName());
    }

    protected AbstractConfigNode getCurrentNode() {
        return currentNode;
    }

    protected void setCurrentNode(AbstractConfigNode currentNode) {
        this.currentNode = currentNode;
    }

    public NodeStateCallback getNodeStateCallback() {
        return nodeStateCallback;
    }

    /**
     * 节点状态回调
     *
     * @param nodeStateCallback 状态回调
     */
    @Override
    public void setNodeStateCallback(NodeStateCallback nodeStateCallback) {
        this.nodeStateCallback = nodeStateCallback;
    }

    public void setCurrentFlowInfo(FlowInfo currentFlowInfo) {
        this.currentFlowInfo = currentFlowInfo;
    }

    /**
     * 创建线程池时线程名称
     *
     * @return 线程名称
     */
    protected abstract String getThreadName();

    /**
     * 执行节点
     *
     * @param node     节点
     * @param flowInfo 当前流程
     */
    @Override
    public void execute(AbstractConfigNode node, FlowInfo flowInfo) {
        if (null == node || null == flowInfo) {
            logger.error("节点或者流程为null,无法执行");
            return;
        }
        if (node.getNodeType() != getAllowExecuteNodeType()) {
            logger.error("节点[{}]不是功能节点,无法在此处进行执行", node.getNodeKey());
            return;
        }
        logger.debug("开始执行:流程流程模版[{}]中的[{}]节点", flowInfo.getFlowTemplateName(), node.getNodeKey());
        if (node.getExecuteState() == ExecuteState.PREPARE) {
            this.executeNode(node, flowInfo, this.getNodeStateCallback());
        } else {
            logger.error("流程模版[{}]实例[{}]节点[{}]状态为[{}]不能重复执行", flowInfo.getFlowTemplateName(),
                    flowInfo.getFlowInstanceName(), node.getNodeType(), node.getExecuteState());
        }

    }

    /**
     * 执行节点,开始调用真正流程执行器实例
     *
     * @param node              执行节点
     * @param flowInfo          流程信息
     * @param nodeStateCallback 执行状态回调通知接口
     */
    protected abstract void executeNode(
            AbstractConfigNode node,
            FlowInfo flowInfo,
            NodeStateCallback nodeStateCallback);

    /**
     * 获取当前节点可以执行的节点类型
     *
     * @return 节点类型
     */
    protected abstract NodeType getAllowExecuteNodeType();

    /**
     * 判断执行器是否可以执行当前节点
     *
     * @param node 节点对象
     * @return true:可以执行,false不能执行
     */
    protected boolean canExecute(AbstractConfigNode node) {
        if (node.getNodeType() != this.getAllowExecuteNodeType()) {
            logger.error("当前执行器不允许执行[{},{}]类型的节点,只能执行[{},{}]类型节点.", node.getNodeType().getName(),
                    node.getNodeType().getCode(), this.getAllowExecuteNodeType().getName(),
                    this.getAllowExecuteNodeType().getCode());
            return true;
        }
        return false;
    }


    /**
     * 通知执行状态
     *
     * @param executeState 执行状态
     */
    protected synchronized void notifyExecuteState(ExecuteState executeState) {
        this.getCurrentNode().setExecuteState(executeState);
        if (null != this.getNodeStateCallback()) {
            this.getNodeStateCallback().notifyNodeExecuteState(this.getCurrentNode(), currentFlowInfo, executeState);
        }
    }
}
